package rtu

import (
	"github.com/tarm/serial"
	"log"
	"sync"
	"tym/modbus"
)

type Client struct {
	mux sync.Mutex

	port *serial.Port
}

func OpenPort(opts ...Option) *Client {
	o := &options{}
	for _, opt := range opts {
		opt(o)
	}

	config := &serial.Config{
		Name:        o.Name,
		Baud:        o.Baud,
		Size:        o.Size,
		ReadTimeout: o.ReadTimeout,
	}

	conn, err := serial.OpenPort(config)
	if err != nil {
		panic(err)
	}

	client := &Client{
		port: conn,
		//mux: sync.Mutex{},
	}

	return client
}

// ReadHoldingRegisters 读取寄存器
func (c *Client) ReadHoldingRegisters(slaveId, address, numRegisters uint16) []uint16 {

	packer := Pack(slaveId, modbus.FuncCodeReadHoldingRegisters, address, numRegisters)

	resp, err := c.Send(packer)
	if err != nil {
		log.Printf("modbus client error: %v", err)
		return nil
	}

	return resp
}

// WriteHoldingRegisters 写寄存器
func (c *Client) WriteHoldingRegisters(slaveId, address, value uint16) []uint16 {
	packer := Pack(slaveId, modbus.FuncCodeWriteHoldingRegisters, address, value)

	resp, err := c.Send(packer)
	if err != nil {
		log.Printf("modbus client error: %v", err)
		return nil
	}

	return resp
}

func (c *Client) Send(pack modbus.Packager) ([]uint16, error) {
	aduRequest := pack.Encode()

	c.mux.Lock()
	defer c.mux.Unlock()

	_, err := c.port.Write(aduRequest)
	if err != nil {
		return nil, err
	}

	buf := make([]byte, 512)
	lens, err := c.port.Read(buf)
	if err != nil {
		return nil, err
	}

	aduResponse := buf[:lens]
	if err = pack.Verify(aduRequest, aduResponse); err != nil {
		return nil, err
	}

	return pack.Decode(aduResponse), nil
}
